﻿interface ILogger {
    debug(msg: string, data?: any): void;
    error(msg: string, data?: any): void;
} 
interface ITester {
    runTest(): void;
}

interface IList<TItem> extends IEnumerable<TItem> {
    //  
    //  Clears all items in the dictionary.
    //
    clear(): void;

    // 
    // Adds a new item to the list.
    push(item: TItem);

    //
    //  Adds all elements to the current list
    //
    addRange(items: TItem[]);

    //
    //  Inserts an item on the given position. If no position is given, the item is added as the first item in the collection.
    //
    insert(item: TItem, position?: number);

    //
    //  Removes the first occurrence of a specific object. If the object was not found, an error is thrown.
    //
    remove(item: TItem, comp?: IComparer<TItem>): TItem;

    //  
    //  Removes the element on the specified position.
    //
    removeAt(position: number): TItem;
}

interface IEnumerable<TItem> {

    //
    //  Reverts all items and returns them in reverted order.
    //
    reverse(): IEnumerable<TItem>;

    //
    //  Sums the collection. If an selector is specified, it is used to get the value to sum.
    //
    sum(selector?: (item: TItem, index: number) => number): number

    //
    //  Returns one element which is fitting the expression. 
    //  If no expression was defined, it takes the only argument in list. In this case there can be only one element in the list, otherwise an error is thrown.
    //  If nothing is found or not item is in the list, or the expression matches to more than one element, the method throws an error.
    //
    single(expr?: (item: TItem, index: number) => boolean): TItem;

    //
    //  Returns one element which is fitting the expression. 
    //  If no expression was defined, it takes the only argument in list. In this case there can be only one element in the list, otherwise an error is thrown.
    //  If nothing is matched, the method returns null. If there is more than one element matching, the method throws an error.
    //
    singleOrDefault(expr?: (item: TItem, index: number) => boolean, defaultValue?: TItem): TItem;

    //
    //  Returns the first element which is fitting the expression. If no expression was defined, it takes the first argument. If nothing is found or not item is in the list, the method throws an error.
    //
    first(expr?: (item: TItem, index: number) => boolean): TItem;

    //
    //  Returns the first item, or when a expression is given it returns the first item which is matching. If no items are existing
    //  or no item is matching, the method returns null.
    //
    firstOrDefault(expr?: (item: TItem, index: number) => boolean): TItem;

    //
    //  Returns the last element which is fitting the expression. If no expression was defined, it takes the last item in the current list. 
    //  If nothing is found or not item is in the list, the method throws an error.
    //
    last(expr?: (item: TItem, index: number) => boolean): TItem;

    //
    //  Returns the last element which is fitting the expression. If no expression was defined, it takes the last item in the current list. 
    //  If nothing is found or not item is in the list, the method returns null.
    //
    lastOrDefault(expr?: (item: TItem, index: number) => boolean, defaultValue?: TItem): TItem;

    //
    //  Returns an value which indicates wherever the given value does exists in the collection. When a comparer is specified,
    //  it is used for comparing the elements while looping.
    //
    contains(value: TItem, comparer?: IComparer<TItem>): boolean;

    //
    //  Counts all elements. If an expression is given, the system counts all elements which are fitting this expression.
    //
    count(expr?: (item: TItem, index: number) => boolean): number;

    //
    //  Iterates over all items and runs the given function. If the function returns false, the enumeration is stopped. 
    //  The method returns true if the enumeration was not stopped, if the enumeration was stopped, the method returns false.
    //
    forEach(toDo: (item: TItem, index: number) => any): boolean;

    //
    //  Returns a list of values which are produced by the mapper function for every containing item.
    //
    select<TResult>(mapper: (item: TItem, index: number) => TResult): IEnumerable<TResult>;

    //
    //  Returns a value wherever the enumeration contains a item which matches the given expression. If no expression is given,
    //  the method returns true when there is any item the collection.
    //
    any(expr?: (item: TItem, index: number) => boolean): boolean;

    //
    //  Returns the current items as an array.
    //
    toArray(): TItem[]

    //
    //  Returns the index of the given item. If an comparer was specified, it is used for finding the item. 
    //  The method returns the 0 based index of the item. If it does not exists, the method returns -1.
    //
    indexOf(item: TItem, comparer?: IComparer<TItem>): number;

    //
    //  Returns all elements which are fitting the specified condition.
    //
    where(expr: (item: TItem, index: number) => boolean): IEnumerable<TItem>;

    //  
    //  Casts all elements in the list and returns a new one. If an mapper function is specified, this is used for generating the output.
    //
    cast<TResult>(mapMethod?: (item: TItem, index: number) => TResult): IEnumerable<TResult>;

    //
    //  Aggregates the current values with the given aggregate method.
    //  If seed is undefined, the first entry of the current list is the first seeding value and will not enumerated, otherwise the aggregate method
    //  starts with the first entry in the current list by using the value of seed as the first aggregate value.
    //
    aggregate<TResult>(func: (item1: TResult, item2: TItem) => TItem, seed?: TResult): TResult;

    //  
    //  Returns an error without the elements on the index until count is reached. 
    //
    skip(count: number): Linq<TItem>;

    //
    //  Returns the element at the given position.
    //
    elementAt(position: number): TItem;

    //
    //  Returns the element at the given position. If the position is not valid, it returns null.
    //
    elementAtOrDefault(position: number, defaultValue?: TItem): TItem;

    //
    //  Produces the set intersection of two sequences
    //
    intersect(intersectList: TItem[], comparer?: IComparer<TItem>): IEnumerable<TItem>;

    //
    //  Joins the two lists by adding the elements. When a zipper was specified, this method is used to join two elements.
    //
    zip<TResult>(zipWith: TItem[], zipper?: (item1: TItem, item2: TItem) => TResult): Linq<TItem>;

    //
    //  Returns the minimal value of all items. You can specify a comparer for doing the comparision.
    //
    min(selector?: (item: TItem, index: number) => any, comparer?: IComparer<TItem>): TItem;

    //
    //  Returns the maximal value of all items. You can specify a comparer for doing the comparision.
    //
    max(selector?: (item: TItem, index: number) => any, comparer?: IComparer<TItem>): TItem;

    //
    //  Conats the two lists.
    //
    concat(withSource: TItem[]): IEnumerable<TItem>;

    //
    //  Returns a distinct list of all elements.
    //
    distinct(comparer?: IComparer<TItem>): Linq<TItem>;

    //
    //  Returns the first count elements of the list.
    //
    take(count: number): IEnumerable<TItem>;

    //
    //  Returns a distinct list of the join of both lists, so every element is containing only one times.
    //
    union(
        listToAdd: any,
        comparer?: IComparer<TItem>): Linq<TItem>;

    // 
    //  Returns a grouped list of the given items. The groupMapper parameter contains a method where the grouped output
    //  is generated. The optional key mapper and key comparer helps generating an comparing the key which are grouped. 
    //
    groupBy<TKey, TElement, TResult>(
        keyMapper: (item: TItem, index: number) => TKey,
        elementSelector?: (item: TItem, index: number) => TElement,
        resultSelector?: (items: TItem[], key: TKey) => TResult,
        keyComparer?: IComparer<TKey>): Dictionary<TKey, TResult>

    //
    //  Groups the elements into a dictionary. The indexer method creates the key for the dictionary.
    //
    toDictionary<TKey>(
        indexer: (item: TItem, index: number) => TKey,
        keyComparer?: IComparer<TKey>): Dictionary<TKey, TItem[]>;

    //
    //  Orders the items by the given expression. You can specify a comparer for comparing the resulting expression values.
    //
    orderBy<TKey>(
        orderByKeyMethod: (item: TItem, index: number) => TKey,
        comparer?: IComparer<TKey>): IEnumerable<TItem>;

    //
    //  Orders the items descending by the given expression. You can specify a comparer for comparing the resulting expression values.
    //
    orderByDescending<TKey>(
        orderByKeyMethod: (item: TItem, index: number) => TKey,
        comparer?: IComparer<TKey>): IEnumerable<TItem>;

    //
    //  Returns a flatten list of values which are produced by the mapper function for every containing item.
    //
    selectMany<TResult>(mapper: (item: TItem, index: number) => TResult): IEnumerable<TResult>;

    //
    //  Returns an value which indicates wherever all elements are matching the given expression.
    //
    all(expression: (item: TItem, index: number) => boolean): boolean;

    //
    //  Returns the given default IEnumerable when the current collection is empty.
    //
    defaultIfEmpty(defaultValue: IEnumerable<TItem>): IEnumerable<TItem>;
}

//
//  C# dictionary like implementation.
//
interface IDictionary<TKey, TValue> extends IList<IKeyValuePair<TKey, TValue>> {

    //
    //  Adds a new entry. 
    //
    add(key: TKey, value: TValue): void;
    //
    //  Returns whenever the key was added to the collection. Optionally you can add a comparer to find the correct key.
    // 
    containsKey(key: TKey, comparer?: IComparer<TKey>): boolean;

    //
    //  Removes the entry with the specified key.
    //
    removeKey(key: TKey, comp?: IComparer<TKey>): TValue;

    //
    //  Returns the first element index of the entry with the matching key.
    //
    indexOfKey(key: TKey, comp?: IComparer<TKey>): number;

    //
    //  tries returning a value with a specific key. If the value does not exists, the method returns null.
    //
    tryGetValue(key: TKey, comp?: IComparer<TKey>): TValue;

    //
    //  Returns an array of all values in the dictionary.
    //
    getValues(): TValue[];

    //  
    //  Returns an array of all keys.
    //
    getKeys(): TKey[];
}

//
//  Comparer are helping compare two values.
//
interface IComparer<TItem> {
    //  
    //  Returns a value which indicates wherever item1 is bigger, smaller or equal with item2. If -1 is returned, item1 is smaller, if 0 is returned, both are equal and if 1 is returned, item1 is bigger than item2.
    //
    compare(item1: TItem, item2: TItem): number;
}

//
//  A pair of key and values.
//
interface IKeyValuePair<TKey, TValue> {
    key: TKey;
    value: TValue;
}